#ifndef VMMODEL_HPP
#define VMMODEL_HPP

#include "VmModel.hpp"
#include "block.hpp"
#include <vector>

VmModel::VmModel(unsigned int page_size, unsigned int vm_n_block,
                 unsigned int n_mem, ALGO algo)
    : page_size(page_size), vm_n_block(vm_n_block), n_mem(n_mem), algo(algo) {
  mem = std::vector<mem_block>(n_mem, mem_block());
}

int VmModel::get_available(int target_num) {
  for (int i = 0; i < n_mem; ++i) {
    if (!mem[i].is_valid || mem[i].num == target_num) {
      return i;
    }
  }
  return -1;
}

int VmModel::evict(std::vector<int> const &requests, int idx) {
  int res = 0;
  if (algo == OPT) {
    // int idx_of_max = 0;
    std::vector<int> tmp(n_mem, idx);
    // tmp.fill(idx);
    for (int i = 0; i < n_mem; ++i) {
      while (tmp[i] < n_mem && mem[i].num != requests[tmp[i]]) {
        tmp[i]++;
      }
    }
    // int res = 0;
    for (int i = 1; i < n_mem; ++i) {
      if (tmp[i] > tmp[res]) {
        res = i;
      }
    }
    // return res;
  } else if (algo == LRU) {
    // int res = 0;
    for (int i = 1; i < n_mem; ++i) {
      if (mem[i].last_used < mem[res].last_used) {
        res = i;
      }
    }
    // return res;
  } else {
    for (int i = 1; i < n_mem; ++i) {
      if (mem[i].create_time < mem[res].create_time) {
        res = i;
      }
    }
  }
  mem[res].is_valid = false;
  return res;
}

int VmModel::simulate(std::vector<int> const &requests,
                      std::vector<std::vector<mem_block>> &res) {

  int time_idx = 0, page_fault = 0;

  for (auto request_num : requests) {
    int target_i;
    if ((target_i = get_available(request_num)) == -1) {
      target_i = evict(requests, time_idx);
    }
    if (!mem[target_i].is_valid) {
      mem[target_i].num = request_num;
      mem[target_i].create_time = time_idx;
      mem[target_i].is_valid = true;
      page_fault++;
    }
    mem[target_i].last_used = time_idx;

    res.push_back(mem);

    time_idx++;
  }
  return page_fault;
}

#endif